package com.jy.api.service.impl;

import com.jy.api.config.WebSocketUrlUtils;
import com.jy.api.dao.*;
import com.jy.api.domain.Employee;
import com.jy.api.domain.Store;
import com.jy.api.domain.StoreRegionConfig;
import com.jy.api.exception.CallServiceException;
import com.jy.api.exception.ExceptionEnum;
import com.jy.api.service.CallService;
import com.jy.api.signUpReq.MarsCallReq.CallReq;
import com.jy.api.util.ISyncExecutor;
import com.jy.api.util.OkHttpUtils;
import com.jy.api.util.RedisSyncLocker;
import com.jy.api.util.Result;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Created by xiaohua on 2018/11/12.
 */
@Service
@Slf4j
public class CallServiceImpl implements CallService {

    @Autowired
    private CallServiceDao callServiceDao;

    private SimpMessagingTemplate simpMessagingTemplate;
    @Autowired
    private StoreDao storeDao;
    @Autowired
    private EmployeeDao e;
    @Autowired
    private RedisSyncLocker redisSyncLocker;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private EmployeeDao employeeDao;
    @Autowired
    private WebSocketUrlUtils urlUtils;
    @Autowired
    RestTemplate restTemplate;
    @Autowired
    private StoreRegionConfigDao storeRegionConfigDao;


    @Override
    @Transactional
    public Map<String, Object> callService(CallReq req) throws Exception {
        return (Map) redisSyncLocker.idLock(req.getEmpId(), new ISyncExecutor() {
            @Override
            public Object execute(Object o) throws Exception {
                Employee employee = e.findOne(req.getEmpId());
                Store store = storeDao.findOne(employee.getStoreId());
                Map<String, Object> map = new HashMap<>();
                if (!employee.isWork()) {
                    map.put("data", null);
                    map.put("result", new Result(400, employee.getName() + "已下班，请更换服务员为您服务。"));
                    return map;
                }
                com.jy.api.domain.CallService callService = new com.jy.api.domain.CallService(req, employee.getPhoto());
                com.jy.api.domain.CallService returnService = callServiceDao.save(callService);
                employee.setCallCount(employee.getCallCount() + 1);
                map.put("result", new Result(200, "呼叫成功，正在为您呼叫" + returnService.getEmpName()));
                map.put("data", returnService);
                redisTemplate.opsForValue().set(employee.getId(), employee.getId(), store.getCallSecond(), TimeUnit.SECONDS);
                notifyCallNewCallService(returnService);
                return map;
            }

        });
    }

    @Override
    @Transactional
    public com.jy.api.domain.CallService callFinish(int id) throws Exception {
        com.jy.api.domain.CallService callService = callServiceDao.findOne(id);

        if (callService == null) {
            throw new CallServiceException("", ExceptionEnum.IS_NULL);
        } else {
            callService.setEndTime(new Date());
            callService.setBusy(false);
        }
        com.jy.api.domain.CallService returnCall = callServiceDao.save(callService);
        return returnCall;
    }

    @Override
    public Map<String, Object> findByStoreCallService(String storeId, Pageable pageable) {
        Map<String, Object> map = new HashMap<>();
        Store store = storeDao.findOne(storeId);
        if (null != store.getCallEndTime()) {
            StoreRegionConfig regionConfig = storeRegionConfigDao.findByStoreId(storeId);

            if (store.isCall() && (new Date().getTime() - store.getCallEndTime().getTime()) < 0) {
                Date before = new Date();
                Date after = get12HoursAfter(before);
                Page<com.jy.api.domain.CallService> list = callServiceDao.findByStoreIdAndCallTimeLessThanEqualAndCallTimeGreaterThanEqualOrderByCallTimeDesc(storeId, before, after, pageable);
                map.put("data", list);
                map.put("call", store.isCall());
                map.put("callTime", store.getCallTime());
                map.put("callEndTime", store.getCallEndTime());
                map.put("logo", store.getStoreLogo());
                map.put("name", store.getName());
                map.put("second", store.getCallSecond());
                map.put("voiceEnabled", store.isVoiceEnabled());
            } else {
                List<String> list = new ArrayList<>();
                map.put("call", "");
                map.put("callTime", "");
                map.put("callEndTime", "");
                map.put("logo", "");
                map.put("name", store.getName());
                map.put("data", list);
                map.put("second", 0);
                map.put("voiceEnabled", false);
            }
            map.put("offOnVoiceNotify", false);
            if (null != regionConfig) {
                map.put("offOnVoiceNotify", regionConfig.isVoiceNotify());
            }
            map.put("posCallEnabled", store.isPosCallEnabled());
        } else {
            List<String> list = new ArrayList<>();
            map.put("call", "");
            map.put("callTime", "");
            map.put("callEndTime", "");
            map.put("logo", "");
            map.put("name", store.getName());
            map.put("data", list);
            map.put("second", 0);
            map.put("voiceEnabled", false);
            map.put("posCallEnabled", store.isPosCallEnabled());
        }
        return map;
    }

    @Override
    @Transactional
    public Map<String, Object> judgeCallService(String empId, String storeId, String tableNo) throws Exception {
        return (Map) redisSyncLocker.idLock(empId, new ISyncExecutor() {
            Employee employee = employeeDao.findOne(empId);

            @Override
            public Object execute(Object o) throws Exception {
                Map<String, Object> map = new HashMap<>();
                if (null == redisTemplate.getExpire(empId)) {
                    map.put("code", 200);
                    return map;
                }
                if (!employee.isWork()) {
                    map.put("code", 400);
                    map.put("msg", employee.getName() + "已下班，请更换服务员为您服务。");
                    return map;
                }
                long value = redisTemplate.getExpire(empId);
                if (value <= 0) {
                    map.put("code", 200);
                    map.put("msg", "");
                    return map;
                } else {
                    map.put("code", 400);
                    map.put("msg", "还剩余" + value + "秒可呼叫" + employee.getName());
                    return map;
                }
            }
        });

    }


    private void notifyCallNewCallService(com.jy.api.domain.CallService service) {
        Employee employee = e.findOne(service.getEmpId());
        try {
            Map<String, String> map = new HashMap<>();
            map.put("source", "CALL");
            map.put("empId", service.getEmpId());
            map.put("workId", employee.getCode());
            map.put("tableNum", service.getTableNum());
            map.put("empName", service.getEmpName());
            map.put("storeId", service.getStoreId());
            map.put("type", "CALL");
            putWs(map);
        } catch (Exception e) {
            log.warn("Fail to send websocket notify to store:{}", service.getStoreId());
        }
    }


    @Autowired
    public void setSimpMessagingTemplate(SimpMessagingTemplate simpMessagingTemplate) {
        this.simpMessagingTemplate = simpMessagingTemplate;
    }


    public Date get12HoursAfter(Date before) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        simpleDateFormat.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        Date date = before;
        String s = simpleDateFormat.format(date);
        Date date1 = null;
        try {
            date1 = simpleDateFormat.parse(s);
        } catch (ParseException e) {
            log.info("时间转换异常");
            e.printStackTrace();
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(date1);
        cal.add(Calendar.HOUR, -36);// 24小时制
        date1 = cal.getTime();
        return date1;
    }

    private void putWs(Map<String, String> map) {
        try {
            //发送ws
            String url = urlUtils.getWs();
            OkHttpUtils.post(url, com.alibaba.fastjson.JSONObject.toJSONString(map));
        } catch (Exception ex) {
            log.error(" Invitation code initialization failed msg:{}", ex);
        }

    }

}
